# asyncio.sleep()
# В данном уроке рассмотрим уже знакомую функцию asyncio.sleep(), у которой, помимо уже известных вам методов применения, 
# есть нестандартные, но полезные варианты применения в реальных асинхронных приложениях.

 

# coroutine asyncio.sleep(delay, result=None) — временно приостанавливает исполнение текущей корутины, позволяя циклу событий
# (event loop) обрабатывать другие корутины или задачи.
#delay: Количество секунд на которое нужно приостановить выполнение. Может быть не только целым числом,
#  но и дробным для указания более точного времени задержки.
#result: (Необязательный) Значение, которое sleep() вернет после завершения задержки. По умолчанию None.
# import asyncio

# async def main():
#     print(await asyncio.sleep(1, result='Сообщение из result'))

# asyncio.run(main())
# Вывод:

# Сообщение из result
# Параметр result в функции asyncio.sleep() может быть полезен, когда вы хотите вернуть некоторый результат или значение 
# после определенной задержки, например, для имитации задержки выполнения асинхронной операции или возвращения фиктивного значения в тестах.

# Особенности asyncio.sleep().
# 1. asyncio.sleep() не блокирует поток выполнения программы, а только приостанавливает выполнение текущей корутины. Это означает,
# что другие корутины могут быть запущены и выполнены в то время, когда текущая корутина находится в состоянии ожидания.

# 2. Не точный метод. Следует помнить, что asyncio.sleep() не является точным методом для задержки выполнения. Например, если мы
# указываем значение delay равным 0.1 секунды, то выполнение корутины может быть приостановлено на более длительное время из-за
# переключения между задачами или других факторов, влияющих на работу event loop. Поэтому, если нам нужно точно контролировать время
# задержки выполнения, то лучше использовать другие средства, например, модуль time.


# Пример использования asyncio.sleep():
# В рамках данного курса мы уже много раз использовали данную функцию, так что просто напомним, как она работает. Допустим, у нас есть список задач, которые нужно выполнить,
# и мы хотим приостановить выполнение каждой задачи на некоторое время. Мы будем использовать asyncio.sleep() для этого:
#import asyncio

# async def task(name, delay):
#     print(f"Запуск задачи {name}")
#     await asyncio.sleep(delay)
#     print(f"Завершение задачи {name}")

# async def main():
#     tasks = [
#         asyncio.create_task(task("-Задача 1-", 1)),
#         asyncio.create_task(task("-Задача 2-", 0.5)),
#         asyncio.create_task(task("-Задача 3-", 2))
#     ]

#     await asyncio.gather(*tasks)

# asyncio.run(main())

#В рамках данного курса asyncio.sleep() в основном используется для имитации выполнения длительной операции, однако у данного метода есть реальные практические применения. 


#Применение asyncio.sleep() может быть полезно в следующих ситуациях:
# Тестирование асинхронного кода: asyncio.sleep() может быть использована для имитации асинхронных операций, которые занимают некоторое
# время для выполнения. Использование этой корутины может оказаться полезным при тестировании вашего кода. Она поможет убедиться,
# что код правильно обрабатывает ожидание.

# Ограничение скорости запросов: если вы делаете много асинхронных запросов к внешнему ресурсу, который имеет ограничения на скорость запросов,
# вы можете использовать asyncio.sleep() для введения задержек между запросами, чтобы не превысить эти ограничения. 

# Симуляция задержек в реальном времени: если вы создаете систему, которая должна имитировать реальные задержки 
# (например, в игре или в системе моделирования), asyncio.sleep() может быть использована для введения этих задержек.

# Планирование задач: если вам нужно запустить определенную задачу через определенный промежуток времени,
# вы можете использовать asyncio.sleep() для введения этой задержки.

# Ожидание внешних ресурсов: если ваш код зависит от внешнего ресурса, который может быть недоступен или занят 
# (например, файл или сетевое соединение), вы можете использовать asyncio.sleep() для введения задержки перед повторной попыткой доступа к ресурсу.

# Синхронизация выполнения нескольких корутин. Вы также можете использовать asyncio.sleep() для задержки выполнения одной корутины до того момента,
# пока не завершится выполнение другой корутины, время выполнения которой нам примерно известно. 

# import asyncio

# async def coroutine1():
#     print("Coroutine 1 started")
#     await asyncio.sleep(1)  # Стандартное время выполнения coroutine1()
#     print("Coroutine 1 done")

# async def coroutine2():
#     print("Coroutine 2 started")
#     await asyncio.sleep(2)  # Задаем задержку в 2 секунды, чтобы coroutine2() завершалась после coroutine1()
#     print("Coroutine 2 done")

# async def main():
#     task1 = asyncio.create_task(coroutine1())
#     task2 = asyncio.create_task(coroutine2())

#     await task1
#     await task2

# asyncio.run(main())

# Если мы знаем, что корутина coroutine1() завершается за 1 секунду, а нам нужно, чтобы coroutine2() завершалась после,
# мы можем установить значение delay в 2 секунды и тем самым гарантировать, что coroutine2() будет завершаться после coroutine1().
# Вывод:
# ​Coroutine 1 started
# Coroutine 2 started
# Coroutine 1 done
# Coroutine 2 done

# Запуск задач и смена контекста. Если вам нужно запустить задачи, добавленные в цикл событий, но не нужно явно ожидать результат
# выполнения задачи, можно использовать await asyncio.sleep(0). В этот момент 
# произойдет переключение контекста и начнется выполнение задач. await asyncio.sleep(0) также можно использовать внутри корутин для возможности смены контекста. 

import asyncio

async def count():
    for i in range(3, 0, -1):
        print(f"Обратный отсчет: {i}")
        await asyncio.sleep(0)

async def main():
    await asyncio.gather(count(), count())

asyncio.run(main())

